﻿Imports System.Xml

Public Class Cell

	Public Sheet As Sheet
	Public Type As ValueType
	Public x, y As Integer

	Enum ValueType
		None
		Formula
		Text
		Number
		DateTime
	End Enum

	Sub New(Parent As Sheet, x As Integer, y As Integer)
		Sheet = Parent
		Me.x = x
		Me.y = y
	End Sub

	ReadOnly Property Rect As Rectangle
		Get
			Return Sheet.DisplayRect(x, y)
		End Get
	End Property

	Sub Paint(e As PaintEventArgs, R As Rectangle, Forecolor As Color, Bold As String, Italic As String)
		R.Offset(1, 1)
		Dim Font = Sheet.Font
		Dim S As New FontStyle
		If Bold = "1" Then S += FontStyle.Bold
		If Italic = "1" Then S += FontStyle.Italic
		If S <> FontStyle.Regular Then Font = New Font(Font, S)
		TextRenderer.DrawText(e.Graphics, FormattedValue, Font, R, Forecolor, Color.Transparent, FormattedAlignment + TextFormatFlags.PreserveGraphicsClipping)
	End Sub

	ReadOnly Property FormattedValue As String
		Get
			If Type = ValueType.Formula Then Return CStr(Formula.Value)
			Return CStr(Value)
		End Get
	End Property

	ReadOnly Property FormattedAlignment As TextFormatFlags
		Get
			Select Case Type
				Case ValueType.Number : Return TextFormatFlags.Right
				Case ValueType.DateTime : Return TextFormatFlags.HorizontalCenter
			End Select
			Return TextFormatFlags.Left
		End Get
	End Property

	Dim Text As String
	Property Value As Object
		Get
			Select Case Type
				Case ValueType.DateTime : Return CDate(Text)
				Case ValueType.Number : Return Val(Text)
			End Select
			Return Text
		End Get
		Set(value As Object)
			Text = value
			If Mid(value, 1, 1) = "=" Then
				Type = ValueType.Formula
				Formula = New Expression(Mid(value, 2))
			ElseIf IsNumeric(value) Then
				Type = ValueType.Number
			ElseIf IsDate(value) Then
				Type = ValueType.DateTime
			Else
				Type = ValueType.Text
			End If
			Sheet.Invalidate(Rect)
		End Set
	End Property

	Dim Formula As Expression

	Class Expression

		Public Text As String

		Dim Operators1 As String = "&*/"
		Dim Operators2 As String = "+-"
		Dim Oprs As String
		Public Exp1 As Expression
		Public Exp2 As Expression
		Public Opr As String

		Sub New(Opr As String, Exp1 As Expression, Exp2 As Expression)
			Me.Opr = Opr
			Me.Exp1 = Exp1
			Me.Exp2 = Exp2
		End Sub

		Sub New(Text As String)
			Oprs = Operators1 & Operators2
			Dim Operands As New List(Of String)
			Dim Operators As New List(Of String)
			Me.Text = Text
			If Text.StartsWith("(") And Text.EndsWith(")") Then Text = Mid(Text, 2, Len(Text) - 2)
			If Text.StartsWith("""") And Text.EndsWith("""") Then Return
			Dim Level = 0
			Dim t = ""
			Dim InQuotes As Boolean = False
			For i = 1 To Text.Length
				Dim c = Text(i - 1)
				If Not InQuotes AndAlso c = """" Then
					InQuotes = True
					t &= c
				ElseIf InQuotes AndAlso Mid(Text, i, 2) = """""" Then
					i += 1
					t &= """"
				ElseIf InQuotes AndAlso c = """" Then
					InQuotes = False
					t &= c
				ElseIf InQuotes Then
					t &= c
				ElseIf c = "(" Then
					Level += 1
					t &= c
				ElseIf c = ")" Then
					Level -= 1
					t &= c
				ElseIf Level = 0 AndAlso Oprs.Contains(c) Then
					Operands.Add(t.Trim)
					Operators.Add(c)
					t = ""
				Else
					t &= c
				End If
			Next
			Operands.Add(t.Trim)
			If Operators.Count > 0 Then
				For i = 0 To Operators.Count - 1
					If i = 0 Then
						Opr = Operators(i)
						Exp1 = New Expression(Operands(i))
						Exp2 = New Expression(Operands(i + 1))
					ElseIf Precedence(Operators(i)) >= Precedence(Operators(i - 1)) Then
						Exp1 = New Expression(Opr, Exp1, Exp2)
						Opr = Operators(i)
						Exp2 = New Expression(Operands(i + 1))
					Else
						Exp2 = New Expression(Operators(i), Exp2, New Expression(Operands(i + 1)))
					End If
				Next
			End If
		End Sub

		ReadOnly Property Value As Object
			Get
				Select Case Opr
					Case "+" : Return Exp1.Value + Exp2.Value
					Case "-" : Return Exp1.Value - Exp2.Value
					Case "*" : Return Exp1.Value * Exp2.Value
					Case "/" : Return If(Exp2.Value = 0, "#Hata!0", Exp1.Value / Exp2.Value)
					Case "&" : Return Exp1.Value & Exp2.Value
				End Select
				If Text.StartsWith("""") And Text.EndsWith("""") Then Return Mid(Text, 2, Text.Length - 2).Replace("""""", """")
				If IsNumeric(Text) Then Return CDec(Text)
				If Form1.IsCell(Text) Then Return Form1.GetCell(Text).Value
				Return Text
			End Get
		End Property

		Private ReadOnly Property Precedence(op As String) As String
			Get
				If Operators2.Contains(op) Then Return 2
				Return 1
			End Get
		End Property

	End Class

	Sub Save(Doc As XmlDocument, Element As XmlElement)
		Dim CellNode As XmlElement = Element.AppendChild(Doc.CreateElement("Cell"))
		CellNode.SetAttribute("Text", Text)
		CellNode.SetAttribute("x", x)
		CellNode.SetAttribute("y", y)
	End Sub

End Class